package core

import (
	"context"
	"gitee.com/yijimu/gin-frame/utils"
	"github.com/spf13/viper"
	"log"
	"os"
	"time"

	"github.com/go-redis/redis/v8"

	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

// InitZap 初始化Logger
func InitZap() {
	l := new(zapcore.Level)
	if err := l.UnmarshalText([]byte("debug")); err != nil { //设置日志level
		log.Fatalln(err)
	}

	options := []zap.Option{ //zap配置项
		//zap.AddCaller(), //显示调用行
		zap.Fields(
			zap.String("app_name", viper.GetString("app.name")), //项目名称
		),
	}

	zapCore := zapcore.NewCore(getEncoder(), getWriteSyncer(), l)

	lg := zap.New(zapCore, options...)

	zap.ReplaceGlobals(lg) // 替换zap包中全局的logger实例，后续在其他包中只需使用zap.L()调用即可
}

// getEncoder 日志输出的格式
func getEncoder() zapcore.Encoder {
	// 调整编码器默认配置
	encoderConfig := zap.NewProductionEncoderConfig()

	encoderConfig.TimeKey = "time" //时间key
	//时间格式
	encoderConfig.EncodeTime = func(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
		encoder.AppendString(t.Local().Format("2006-01-02 15:04:05.000"))
	}

	encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder //大写编码器
	encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder
	encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder

	return zapcore.NewJSONEncoder(encoderConfig) //采用json方式
}

// getWriteSyncer 根据配置文件获取日志输出方式： redis、文件 或 控制台 中
func getWriteSyncer() zapcore.WriteSyncer {
	if viper.GetString("app.env") == "release" { //正式环境下日志写入到文件中，测试环境下打印到控制台中
		if viper.GetString("logger.type") == "redis" {
			//日志存储到redis中
			return writerToRedis(
				viper.GetString("logger.redis.key"),
				viper.GetString("logger.redis.addr"),
				viper.GetString("logger.redis.password"),
				viper.GetInt("logger.redis.db"),
			)
		}

		//日志存储到文件中
		return writerToFlie(
			viper.GetString("logger.file.rootDir"),
			viper.GetString("logger.file.fileName"),
			viper.GetInt("logger.file.maxSize"),
			viper.GetInt("logger.file.maxBackups"),
			viper.GetInt("logger.file.maxAge"),
			viper.GetBool("logger.file.compress"),
		)
	}

	return zapcore.AddSync(os.Stdout) //直接输入到控制台
}

// writerToFlie 存储到文件（使用Lumberjack进行日志切割归档）
func writerToFlie(rootDir, filename string, maxSize, maxBackup, maxAge int, compress bool) zapcore.WriteSyncer {
	// 创建日志根目录
	utils.CreateDir(rootDir)

	lumberJackLogger := &lumberjack.Logger{
		Filename:   rootDir + "/" + filename,
		MaxSize:    maxSize,   //日志文件的最大大小（以MB为单位），超过后自动分割
		MaxBackups: maxBackup, //保留旧文件的最大个数,多于 maxBackup 个日志文件后，清理较旧的日志
		MaxAge:     maxAge,    //保留旧文件的最大天数
		Compress:   compress,  //是否压缩/归档旧文件
	}

	return zapcore.AddSync(lumberJackLogger)
}

// writerToRedis 存储到redis：将日志存入到key为 logListKey 的队列list中
func writerToRedis(logListKey string, addr string, password string, db int) zapcore.WriteSyncer {
	cli := redis.NewClient(&redis.Options{
		Addr: addr, // 要连接的redis IP:port
		//Username: "default",
		Password: password, // redis 密码
		DB:       db,       // 要连接的redis 库
	})

	return zapcore.AddSync(&redisWriter{
		cli:     cli,
		listKey: logListKey,
	})
}

// 为 zap 提供写入 redis 队列的 io 接口
type redisWriter struct {
	cli     *redis.Client
	listKey string
}

func (w *redisWriter) Write(p []byte) (int, error) {
	n, err := w.cli.RPush(context.Background(), w.listKey, p).Result()
	return int(n), err
}
